package me.alex.learnkotlin.learnKT9

/**
 * 学习 高阶函数 和 内联函数
 * 这一章节概念较多 需要自行百度/Google关键字学习更多概念知识
 */
fun main() {

    //1.1 函数类型
    /*
    (String, Int) -> Unit
    -> 左边表示该函数接受什么参数,多个参数用逗号分开,如果没有任何参数就只写空括号
    -> 右边表示 声明该函数的返回值是什么类型 如果没有返回值就使用 Unit 相当于java的 void
     */
    //高阶函数示例 example中接收了一个函数类型的func参数
    fun example(func: (String, Int) -> Unit) {
        func("高阶函数", 123)
    }
    //调用
    example { s, i -> println("$s , $i") }

    //1.2 高阶函数的使用
    val num1 = 100
    val num2 = 80

    // ::函数名  是固定写法
    val result1 = num1AndNum2(num1, num2, ::plus)
    println("result1 is $result1")
    val result2 = num1AndNum2(num1, num2, ::minus)
    println("result2 is $result2")

    //1.3 使用lambda写法 这里可以不写具体函数名,而使用lambda自由操作参数
    val result11 = num1AndNum2(num1, num2) { n1, n2 ->
        n1 + n2
    }
    val result22 = num1AndNum2(num1, num2) { n1, n2 ->
        n1 - n2
    }
    println("result11 is $result11")
    println("result22 is $result22")

    /**
     * 这里高阶函数更像是一种高级的工厂模式
     * 就像是原本我们的工厂模式是一个炸鸡厨房,只要丢进去鸡肉,就能出来炸鸡
     * 而高阶函数就像是一个万能的厨房,我们不仅送进去了食材,还送进去了不同的厨师
     * 送进去猪肉加东北厨师,就出来溜肉段/锅包肉
     * 送进去鸡肉加广东厨师,就出来白斩鸡/炖鸡汤
     * 甚至配合lambda可以动态指挥厨师,送进去肉丝以后,一会做京酱肉丝,一会做鱼香肉丝
     */

    //1.4 进阶使用
    /**
     * {@link LearnKotlin6} 中我们学了吃水果的各种方式
     * 这里使用高阶函数实现 具体实现看{@link HigherOrderFunction}
     */
    val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape")
    val result = StringBuilder().build {
        append("开始吃水果 \n")
        for (fruit in list) {
            append(fruit).append("\n")
        }
        append("吃完了")
    }
    println(result.toString())

    //1.5 内联函数
    /**
     * 高阶函数会让编译器把kotlin转换成Java的语法结构 (详情自行百度 高阶函数的实现)
     * 进而使每个高阶函数都会创建一个新的匿名类实例,造成额外的内存和性能开销
     * 为了避免这种问题,使用 inline关键字,就会将参数中的函数替换至lambda的实现方式中,进而减少内存消耗
     * 参考{@see HigherOrderFunction}中的写法
     */
    /*
    使用方式: 注意inline关键字
    inline fun num1AndNum2ForInline(num1: Int, num2: Int, operation: (Int, Int) -> Int): Int {
        val result = operation(num1, num2)
        return result
    }
     */
}

//1.6 oninline 和 crossinline
/*
    使用inline关键字可以让编译器把所有引用的lambda都进行内联,
    如果有多个函数参数,只想内联其中一个,就可以使用noinline关键字,
    比如:
 */
inline fun inlineTest(block1: () -> Unit, noinline block2: () -> Unit) {

}

fun printString(string: String, block: (String) -> Unit) {
    println("printString start")
    block(string)
    println("printString end")
}

inline fun printString1(string: String, block: (String) -> Unit) {
    println("printString start")
    block(string)
    println("printString end")
}



fun main(args: Array<String>) {
    println("main start")
    val str = ""
    printString(str) { s ->
        println("lambda start")
        if (s.isEmpty()) return@printString     //使用return@printString的写法 表示局部返回,返回至100行,相当于java的 break:A类标识
        println(s)
        println("lambda end")
    }
    println("main end")
    /*
        这里打印的结果 101行还是执行了 理想情况应该是更简单的写法
        于是使用inline关键字这么写 注意pln中增加了1
     */
    println("main start1")
    val str1 = ""
    printString1(str1) { s ->
        println("lambda start1")
        if (s.isEmpty()) return
        println(s)
        println("lambda end1")
    }
    println("main end1")

    /*
        把高阶函数声明成内联函数是好习惯,绝大多数高阶函数都可以声明为内联函数,但是少部分除外
        例:
        inline fun runRunnable(block: () -> Unit) {
        val runnable = Runnable {
                block()
            }
        }
        这里会报错,需要使用crossinline关键字解决
        inline fun runRunnable(crossinline block: () -> Unit) {
            val runnable = Runnable {
                block()
            }
        }
     */


}

